﻿namespace WingBeats.Xml

type tagName = string

exception NodeCanNotHoldInnerText

[<CustomEquality; NoComparison>]
type Node =
    | TagPairNode     of tagName * (string * string) list * seq<Node> 
    | SelfClosingNode of tagName *  (string * string) list
    | Text         of string
    | LiteralText  of string
    | Placeholder  of string
    | NoNode
    member this.InnerText =
        let sb = System.Text.StringBuilder()
        let rec getText node =
            match node with
            | TagPairNode(_,_,inner) -> inner |> Seq.iter getText
            | LiteralText s
            | Text        s          -> sb.Append(s) |> ignore
            | _                      -> ()
        getText this
        sb.ToString()
    member this.Nodes =
        match this with
        | TagPairNode (_, _, nodes) -> nodes
        | _ -> Seq.empty
    member this.Attributes =
        match this with
        | TagPairNode (_, attr, _)
        | SelfClosingNode (_, attr) -> attr
        | _ -> List.empty    
    member this.TagName =
        match this with
        | TagPairNode (name, _, _)
        | SelfClosingNode (name, _) -> name
        | _ -> System.String.Empty
    override this.GetHashCode() = 
        match this with
        | TagPairNode (tag, attribs, nodes) 
            -> let nodesHash = nodes |> Seq.fold (fun acc cur -> acc ^^^ cur.GetHashCode()) 0
               11 ^^^ tag.GetHashCode() ^^^ attribs.GetHashCode() ^^^ nodesHash
        | SelfClosingNode (tag, attribs)
            -> 13 ^^^ tag.GetHashCode() ^^^ attribs.GetHashCode() 
        | Text str
            -> 17 ^^^ str.GetHashCode()
        | LiteralText str
            -> 19 ^^^ str.GetHashCode()
        | Placeholder str
            -> 23 ^^^ str.GetHashCode()
        | NoNode
            -> 29 ^^^ System.String.Empty.GetHashCode()
    override this.Equals(other : obj) = 
        match other with
        | :? Node as n ->
            match this, n with
            | TagPairNode(t1, a1, n1), TagPairNode(t2, a2, n2) 
                -> t1 = t2 
                    && (List.sortBy fst a1) = (List.sortBy fst a2) 
                        && (Seq.length n1) = (Seq.length n2)  
                            && Seq.forall2 (fun x y -> x.Equals(y)) n1 n2
            | SelfClosingNode (t1, a1), SelfClosingNode (t2, a2)
                -> t1 = t2 && (List.sortBy fst a1) = (List.sortBy fst a2)
            | Text(s1), Text(s2) -> s1 = s2
            | LiteralText(s1), LiteralText(s2) -> s1 = s2
            | Placeholder(s1), Placeholder(s2) -> s1 = s2
            | _, _ -> false
        | _ -> false
